#include "PeopleManager.h"
#include "TeamManager.h"
#include "People.h"
#include "ServiceProvider.h"
#include "GameSettings.h"
#include "LeagueManager.h"
#include "NameManager.h"
#include "Team.h"
#include "PeopleFunctors.h"
#include "ZStream.h"
#include "RandomSystem.h"
#include <cassert>

size_t PeopleManager::NUMBER_OF_MEMBERS = 0;
const unsigned int PeopleManager::BIG_SLOT_MEMBERS = 100;
const unsigned int PeopleManager::SMALL_SLOT_MEMBERS = 10;

PeopleManager::PeopleManager(ServiceProvider* const i_sp) : m_sp(i_sp)
{
	assert(m_sp);
}

PeopleManager::~PeopleManager()
{
	Clear();
}

void PeopleManager::Clear()
{
	Utilities::DeepClear(m_freePlayers);
	Utilities::DeepClear(m_busyPlayers);
	Utilities::DeepClear(m_freeTrainers);
	Utilities::DeepClear(m_busyTrainers);
	Utilities::DeepClear(m_hof);

	NUMBER_OF_MEMBERS = 0;

	People::s_params.max_skill_value = 150;
}

void PeopleManager::Initialize()
{
	NUMBER_OF_MEMBERS = 0;

	People::s_params.MAX_CHAR_NAME			= m_sp->GetSettings().GetValue("I_MAX_CHAR_PLAYER_NAME").Get<size_t>();
	People::s_params.MIN_SKILL_VALUE		= m_sp->GetSettings().GetValue("I_MIN_SKILL_VALUE").Get<size_t>();
#ifdef DARIO
	People::s_params.SELL_FACTOR			= m_sp->GetSettings().GetValue("I_PLAYER_SELL_FACTOR");
	People::s_params.BUY_FACTOR				= m_sp->GetSettings().GetValue("I_PLAYER_BUY_FACTOR");
#endif
	People::s_params.max_skill_value		= 150;

	People::s_params.DAILY_FATIGUE_RECOVER	= m_sp->GetSettings().GetValue("I_DAILY_FATIGUE_RECOVER");
	//People::s_params.FATIGUE_UPDATE[GoalkeeperRole] = 1 + (Rand()%15)/10;
	//People::s_params.FATIGUE_UPDATE[DefenderRole] = 1 + (Rand()%25)/10;
	//People::s_params.FATIGUE_UPDATE[MidfieldRole] = 2 + (Rand()%20)/10;
	//People::s_params.FATIGUE_UPDATE[AttackerRole] = 1 + (Rand()%30)/10;
	People::s_params.FATIGUE_UPDATE[GoalkeeperRole] = 1;
	People::s_params.FATIGUE_UPDATE[DefenderRole] = 2;
	People::s_params.FATIGUE_UPDATE[MidfieldRole] = 3;
	People::s_params.FATIGUE_UPDATE[AttackerRole] = 2;
	People::s_params.FATIGUE_UPDATE[NUM_FORMATION_ROLES] = -People::s_params.DAILY_FATIGUE_RECOVER;

	PeopleHistory::s_currentYear = m_sp->GetSettings().GetValue("I_STARTING_YEAR");
}

void PeopleManager::ChangePeopleRole(People* i_people, const int i_role)
{
	if(i_people->IsPlayer())
		i_people->m_skill.m_playerSkills.SetRole(i_role);
	else if(i_people->IsTrainer())
		i_people->m_skill.m_trainerSkills.SetRole(i_role);
	else
		assert(0);
}

People* PeopleManager::GetNewPeople(const bool i_isPlayer)
{
	PeopleVec& freeVec = i_isPlayer ? m_freePlayers : m_freeTrainers;
	PeopleVec& busyVec = i_isPlayer ? m_busyPlayers : m_busyTrainers;
	if(freeVec.size() < BIG_SLOT_MEMBERS)
		GenerateNewSlot(BIG_SLOT_MEMBERS, i_isPlayer);

	const int random = Rand()%freeVec.size();
	People* p = freeVec.at(random);
	freeVec.erase(freeVec.begin() + random);
	busyVec.push_back(p);

#ifndef DARIO
	//add contract
	p->SetContract(p->CalculateExpectedSalary(), Rand()%5+1);
#endif

	return p;
}

People* PeopleManager::CreateNewPeople(const bool i_isEmpty, const bool i_isBusy, const bool i_isPlayer)
{
	People* p = i_isEmpty 
					? new People() 
					: new People(m_sp->GetNameMgr().GetAPeopleName(), GetNewID(), i_isPlayer);

	i_isBusy
		? (i_isPlayer ? m_busyPlayers : m_busyTrainers).push_back(p)
		: (i_isPlayer ? m_freePlayers : m_freeTrainers).push_back(p);

	p->m_history.SetBusy(i_isBusy);

	return p;
}

void PeopleManager::CreateFreePeople(const unsigned int i_players, const unsigned int i_trainers)
{
	GenerateNewSlot(i_players, true);
	GenerateNewSlot(i_trainers, false);

	Utilities::Sort(m_freePlayers, PeopleBuyChoose);
	//Utilities::Sort(m_freeTrainers, PeopleBuyChoose);
}

void PeopleManager::GenerateNewSlot(const unsigned int i_size, const bool i_isPlayer)
{
	PeopleVec& freeVec = i_isPlayer ? m_freePlayers : m_freeTrainers;
	freeVec.reserve(freeVec.size() + i_size);
	for(size_t i = 0; i < i_size; ++i)
		CreateNewPeople(false, false, i_isPlayer);
}

//todo refactoring with template
People*	PeopleManager::GetPeopleFromID(const int i_peopleID, const int i_status)
{
	PeopleVecIt it;

	if((i_status & StatusPlayer) != 0)
	{
		it = STL::find_if(m_busyPlayers.begin(), m_busyPlayers.end(), PeopleComparisonID(i_peopleID));
		if(it != m_busyPlayers.end())
			return (*it);

		it = STL::find_if(m_freePlayers.begin(), m_freePlayers.end(), PeopleComparisonID(i_peopleID));
		if(it != m_freePlayers.end())
			return (*it);
	}

	if((i_status & StatusTrainer) != 0)
	{
		it = STL::find_if(m_busyTrainers.begin(), m_busyTrainers.end(), PeopleComparisonID(i_peopleID));
		if(it != m_busyTrainers.end())
			return (*it);

		it = STL::find_if(m_freeTrainers.begin(), m_freeTrainers.end(), PeopleComparisonID(i_peopleID));
		if(it != m_freeTrainers.end())
			return (*it);
	}

	it = STL::find_if(m_hof.begin(), m_hof.end(), PeopleComparisonID(i_peopleID));
	if(it != m_hof.end())
		return (*it);

	//comment because playerID not found in m_hof after about 20 years (function called in TeamHistoryMenu)
	//assert(false);
	return 0;
}

void PeopleManager::Ingage(People* i_people, const bool i_isPlayer)
{
	PeopleVec& freeVec = i_isPlayer ? m_freePlayers : m_freeTrainers;
	PeopleVec& busyVec = i_isPlayer ? m_busyPlayers : m_busyTrainers;

	assert(STL::find(busyVec.begin(), busyVec.end(), i_people) == busyVec.end());
	busyVec.push_back(i_people);
	i_people->m_history.SetBusy(true);
	Utilities::erase_unordered(freeVec, i_people); //RemoveAll_unordered(freeVec, i_people);
}

void PeopleManager::Sell(People* i_people, const bool i_isPlayer) 
{
	PeopleVec& freeVec = i_isPlayer ? m_freePlayers : m_freeTrainers;
	PeopleVec& busyVec = i_isPlayer ? m_busyPlayers : m_busyTrainers;

	assert(STL::find(freeVec.begin(), freeVec.end(), i_people) == freeVec.end());
	freeVec.push_back(i_people);
	i_people->m_history.SetBusy(false);
	i_people->SetTeamID(Utilities::INVALID_ID);
#ifndef DARIO
	i_people->SetContract(0,0);
#endif

	PeopleVecIt it = STL::find(busyVec.begin(), busyVec.end(), i_people);
	assert(it != busyVec.end());
	busyVec.erase(it);
}

void PeopleManager::Update()
{
	static People* s_emptyTrainers[Team::NUM_TRAINERS_ROLES] = {0,0,0,0};

	//only for players
	for(PeopleVecIt it = m_freePlayers.begin(); it!= m_freePlayers.end(); ++it)
		(*it)->PlayerPostMatch(NUM_FORMATION_ROLES,0,0,0,false,0, false, s_emptyTrainers);

	//TODO: something for trainers

	// Market Management
	CreateFreePeople(SMALL_SLOT_MEMBERS, 1);
}

void PeopleManager::CreateHistoryEntry(People* i_people)
{
	//TODO: recheck and add support for trainers
	i_people->m_history.SetBusy(true);
	i_people->m_history.addHistoryEntry(PeopleHistoryRecord(PeopleHistory::s_currentYear, i_people->GetTeamID()));
}

void PeopleManager::EndSeasonUpdate(const bool i_doNotPrompt, STD::stringstream& o_message)
{
	++PeopleHistory::s_currentYear;
	if(People::s_params.max_skill_value < 255)
		++People::s_params.max_skill_value;

	PeopleVec retiredPlayers;
	PeopleVec playersBecometrainers;
	PeopleVec retiredTrainers;

	PeopleVec endContractTrainer;
	EndSeasonUpdate(false, retiredTrainers, endContractTrainer);
	
	PeopleVec endContractPlayer;
	EndSeasonUpdate(true, retiredPlayers, endContractPlayer);
	PlayersBecomeTrainers(retiredPlayers, playersBecometrainers); // side effect: some retiring player become trainer

	if(!i_doNotPrompt)
	{
		Utilities::Sort(retiredTrainers, PeopleComparisonSalary);
		o_message << STD::endl;
		for(auto i = retiredTrainers.begin(); i != retiredTrainers.end(); ++i)
			o_message << (*i)->GetName() << " Retired as Trainer at the age of " << (*i)->GetHistory().GetAge() << STD::endl;

		Utilities::Sort(playersBecometrainers, PeopleComparisonSalary);
		o_message << STD::endl;
		for(auto i = playersBecometrainers.begin(); i != playersBecometrainers.end(); ++i)
			o_message << (*i)->GetName() << " Retired as Player at the age of " << (*i)->GetHistory().GetAge() << " and started his Trainer career" << STD::endl;

		Utilities::Sort(retiredPlayers, PeopleComparisonSalary);
		o_message << STD::endl;
		for(auto i = retiredPlayers.begin(); i != retiredPlayers.end(); ++i)
			o_message << (*i)->GetName() << " Retired as Player at the age of " << (*i)->GetHistory().GetAge() << STD::endl;
	}

	ShrinkHof(retiredPlayers, retiredTrainers); //deep delete of non hof peoples, integrate newly retired in the hof
}

void PeopleManager::EndSeasonUpdate(const bool i_isPlayer, PeopleVec& o_retiringPeople, PeopleVec& o_endContractPeople)
{
	PeopleVec& freeVec = i_isPlayer ? m_freePlayers : m_freeTrainers;
	PeopleVec& busyVec = i_isPlayer ? m_busyPlayers : m_busyTrainers;

	o_retiringPeople.clear();
	o_endContractPeople.clear();

	for(PeopleVecIt it = busyVec.begin(); it!= busyVec.end(); ++it)
	{
		if((*it)->SeasonUpdate())
		{
			o_retiringPeople.push_back(*it);
			*it = 0;
		}
		else
		{
			CreateHistoryEntry(*it);

#ifndef DARIO
			if(!(*it)->HasContract())
			{
				o_endContractPeople.push_back(*it);
			}
#endif
		}
	}

	Utilities::RemoveAll_unordered(busyVec, static_cast<People*>(0));

	for(PeopleVecIt it = freeVec.begin(); it!= freeVec.end(); ++it)
	{
		if((*it)->SeasonUpdate())
		{
			o_retiringPeople.push_back(*it);
			*it = 0;
		}
	}
	Utilities::RemoveAll_unordered(freeVec, static_cast<People*>(0));

	for(PeopleVecIt it = o_retiringPeople.begin(); it!= o_retiringPeople.end(); ++it)
	{
		People* people = *it;
		people->m_history.SetRetirementYear(i_isPlayer ? StatusPlayer : StatusTrainer);
		Team* peopleTeam = m_sp->GetTeamMgr().GetTeam(people->GetTeamID());
		if(peopleTeam && !peopleTeam->RemovePeople(people)) // team has minimum people yet...
		{
			People* newPeople = GetNewPeople(i_isPlayer);
			const bool peopleAdded = peopleTeam->AddPeople(newPeople);
			assert(peopleAdded); peopleAdded;
			const bool peopleRemoved = peopleTeam->RemovePeople(people);
			assert(peopleRemoved); peopleRemoved;
		}
		people->m_history.SetStatus(StatusRetired);
		people->m_history.SetBusy(false);
	}

#ifndef DARIO
	for(PeopleVecIt it = o_endContractPeople.begin(); it!= o_endContractPeople.end(); ++it)
	{
		//TODO
		Team* team = m_sp->GetTeamMgr().GetTeam((*it)->GetTeamID());
		if(!team->ContractExpires((*it), i_isPlayer))
		{
			People* newPeople = GetNewPeople(i_isPlayer);
			newPeople->SetContract(newPeople->CalculateExpectedSalary(), 1);
			const bool peopleAdded = team->AddPeople(newPeople);
			assert(peopleAdded); peopleAdded;
			const bool peopleRemoved = team->ContractExpires(*it, i_isPlayer);
			assert(peopleRemoved);
		}
	}
#endif
}

void PeopleManager::HandleBankrupt(Team* const io_bankruptTeam)
{
	for (size_t i=0; i<Team::GetParams().STARTING_TEAM_PLAYERS; ++i)
	{
		io_bankruptTeam->AddPeople(GetNewPeople(true), false);

		while (io_bankruptTeam->GetNumPlayers() > Team::GetParams().STARTING_TEAM_PLAYERS)
		{
			People* peo = io_bankruptTeam->GetPlayersList().at(0);
			Utilities::erase_unordered(m_busyPlayers, peo); //Utilities::RemoveAll_unordered(m_busyPlayers, peo);
			m_freePlayers.push_back(peo);
			io_bankruptTeam->RemovePeople(peo);
			peo->m_history.SetBusy(false);
		}
	}

	for (size_t i=0; i<Team::GetParams().STARTING_TEAM_TRAINERS; ++i)
	{
		io_bankruptTeam->AddPeople(GetNewPeople(false), false);

		while (io_bankruptTeam->GetNumTrainers() > Team::GetParams().STARTING_TEAM_TRAINERS)
		{
			People* teo = io_bankruptTeam->GetTrainersList().at(0);
			Utilities::erase_unordered(m_busyTrainers, teo); //Utilities::RemoveAll_unordered(m_busyTrainers, teo);
			m_freeTrainers.push_back(teo);
			io_bankruptTeam->RemovePeople(teo);
			teo->m_history.SetBusy(false);
		}
	}
}

void PeopleManager::PlayersBecomeTrainers(PeopleVec& io_retiringPlayers, PeopleVec& o_playerBecomeTrainer)
{
	PeopleVecIt it= io_retiringPlayers.begin();
	while(it!= io_retiringPlayers.end())
	{
		if((*it)->BecomeTrainer()) //TODO: better criterium :)
		{
			(*it)->m_history.SetStatus(StatusTrainer);
			m_freeTrainers.push_back(*it);
			o_playerBecomeTrainer.push_back(*it);
			it = io_retiringPlayers.erase(it);
		}
		else
		{
			(*it)->m_history.SetRetirementYear(StatusTrainer);
			++it;
		}
	}
}

#define SORT_AND_KEEP(x) Utilities::Sort(m_hof, x); toKeep.insert(m_hof.begin(), m_hof.begin() + NUM_HOF_PEOPLE);

void PeopleManager::ShrinkHof(PeopleVec& io_retiringPlayers, PeopleVec& io_retiringTrainers)
{
	PreShrinkHofPlayers(io_retiringPlayers);
	PreShrinkHofTrainers(io_retiringTrainers);

	Utilities::Append(m_hof, io_retiringPlayers);
	Utilities::Append(m_hof, io_retiringTrainers);

	static const int NUM_HOF_PEOPLE = People::NUM_HOF_PEOPLE;

	if(m_hof.size() <= NUM_HOF_PEOPLE)
		return;

	STL::set<People*> toKeep;

	// people to keep as great players

	SORT_AND_KEEP(PeopleComparisonPlayerAgeUp);
	SORT_AND_KEEP(PeopleComparisonNumSeason(StatusPlayer));
	SORT_AND_KEEP(PeopleComparisonTotalNumInjury);
	SORT_AND_KEEP(PeopleComparisonTotalDayInjury);
	SORT_AND_KEEP(PeopleComparisonMaxSkill(Goalkeeper));
	SORT_AND_KEEP(PeopleComparisonMaxSkill(Defender));
	SORT_AND_KEEP(PeopleComparisonMaxSkill(Attacker));
	SORT_AND_KEEP(PeopleComparisonTotalDisciplinePoints);
	SORT_AND_KEEP(PeopleComparisonTotalMatch(StatusPlayer));
	SORT_AND_KEEP(PeopleComparisonTotalGoal);
	SORT_AND_KEEP(PeopleComparisonTotalGoalRatio);
	SORT_AND_KEEP(PeopleComparisonTotalEvaluation);
	SORT_AND_KEEP(PeopleComparisonTotalMoM);

	// people to keep as great trainers

	SORT_AND_KEEP(PeopleComparisonNumSeason(StatusTrainer));
	SORT_AND_KEEP(PeopleComparisonTotalMatch(StatusTrainer));
	
	// people to keep as great trainers and players (but maybe not individually great as trainer or player)

	SORT_AND_KEEP(PeopleComparisonNumSeason(StatusPlayerOrTrainer));
	SORT_AND_KEEP(PeopleComparisonTotalMatch(StatusPlayerOrTrainer));

	// shrink

	Utilities::Sort(m_hof);
	PeopleVecIt vit = m_hof.begin();
	for(STL::set<People*>::iterator it= toKeep.begin(); it!= toKeep.end(); ++it)
	{
		while(*it != *vit)
		{
			delete *vit;
			*vit = 0;
			++vit;
		}
		++vit;
	}
	while(vit != m_hof.end())
	{
		delete *vit;
		*vit = 0;
		++vit;
	}

	Utilities::RemoveAll_unordered(m_hof, static_cast<People*>(0));
	assert(m_hof.size() == toKeep.size());
	Utilities::Sort(m_hof, PeopleComparisonTotalMatch(StatusPlayerOrTrainer));
}

void PeopleManager::PreShrinkHofPlayers(PeopleVec& io_retiringPlayers)
{
	for(size_t i=0; i<io_retiringPlayers.size(); ++i)
	{
		const PeopleHistory& hist = io_retiringPlayers[i]->GetHistory();
		if ((hist.GetTotalScoredGoal() < 100) && (hist.GetTotalMatch(StatusPlayer) < 200))
		{
			delete io_retiringPlayers[i];
			io_retiringPlayers[i] = 0;
		}
	}
	Utilities::RemoveAll_unordered(io_retiringPlayers, static_cast<People*>(0));
}

void PeopleManager::PreShrinkHofTrainers(PeopleVec& io_retiringTrainers)
{
	for(size_t i=0; i<io_retiringTrainers.size(); ++i)
	{
		const PeopleHistory& hist = io_retiringTrainers[i]->GetHistory();
		if ((hist.GetTotalScoredGoal() < 100) && (hist.GetTotalMatch(StatusPlayer) < 200) // player conditions
			&& (hist.GetTotalMatch(StatusTrainer) < 100)  // trainer conditions
			&& (hist.GetTotalMatch(StatusPlayerOrTrainer) < 150))  // combined
		{
			delete io_retiringTrainers[i];
			io_retiringTrainers[i] = 0;
		}
	}
	
	Utilities::RemoveAll_unordered(io_retiringTrainers, static_cast<People*>(0));
}

void PeopleManager::SerializePeopleArray(ZStream& o_stream, const STL::string& i_tag, const PeopleVec& i_peopleArray) const
{
	o_stream.WriteOpenTag(i_tag.c_str());
	o_stream.WriteAttribute((i_tag + "Num").c_str(), i_peopleArray.size());
	for (PeopleVecCit it = i_peopleArray.begin(); it != i_peopleArray.end(); ++it)
		(*it)->Serialize(o_stream);
	o_stream.WriteCloseTag(i_tag.c_str());
}

void PeopleManager::DeserializePeopleArray(ZStream& i_stream, PeopleVec& o_peopleArray)
{
	i_stream.SkipLine();
	size_t num = 0;
	i_stream.ReadAttribute(num);
	o_peopleArray.clear();
	o_peopleArray.reserve(num);
	for (size_t i = 0; i < num; ++i)
	{
		People* p = new People();
		p->Deserialize(i_stream);
		o_peopleArray.push_back(p);
	}
	i_stream.SkipLine();
}

ZStream& PeopleManager::Serialize(ZStream& o_stream) const
{
	o_stream.WriteOpenTag("PlayerManager");

	o_stream.WriteAttribute("CurrentID", NUMBER_OF_MEMBERS);
	o_stream.WriteAttribute("CurrentSeason", PeopleHistory::s_currentYear);

	SerializePeopleArray(o_stream, "BusyPlayers", m_busyPlayers);
	SerializePeopleArray(o_stream, "FreePlayers", m_freePlayers);
	SerializePeopleArray(o_stream, "BusyTrainers", m_busyTrainers);
	SerializePeopleArray(o_stream, "FreeTrainers", m_freeTrainers);
	SerializePeopleArray(o_stream, "Hof", m_hof);

	o_stream.WriteCloseTag("PlayerManager");

	return o_stream;
}

ZStream& PeopleManager::Deserialize(ZStream& i_stream)
{
	i_stream.SkipLine();

	i_stream.ReadAttribute(NUMBER_OF_MEMBERS);
	i_stream.ReadAttribute(PeopleHistory::s_currentYear);

	People::s_params.max_skill_value = 150 + PeopleHistory::s_currentYear;
	if(People::s_params.max_skill_value > 255)
		People::s_params.max_skill_value = 255;

	DeserializePeopleArray(i_stream, m_busyPlayers);
	DeserializePeopleArray(i_stream, m_freePlayers);
	DeserializePeopleArray(i_stream, m_busyTrainers);
	DeserializePeopleArray(i_stream, m_freeTrainers);
	DeserializePeopleArray(i_stream, m_hof);

	i_stream.SkipLine();

	return i_stream;
}

